var Web3 = require('web3')
var express = require('express')
var app = express()
var server = require('http').createServer(app)
var io = require('socket.io')(server)
var bodyParser = require('body-parser')
var BigNumber = require('bignumber.js')
import { AppConfig } from './app.config.js'
var _async = require('asyncawait/async')
var _await = require('asyncawait/await')
const API_CTX = '/api'
_async(() => {
    const MongoClient = require('mongodb').MongoClient
    const mongoDBUrl = 'mongodb://localhost:27017'
    // Database Name
    const dbName = 'biz_eth_db'
    let mongoDb = null
    MongoClient.connect(mongoDBUrl, function(err, client) {
        mongoDb = client.db(dbName)
        console.log('Connected successfully to server')
    })
    server.listen(AppConfig.port)
    app.use(
        bodyParser.urlencoded({
            extended: false
        })
    )

    // parse application/json
    app.use(bodyParser.json())
    app.use(express.static('public'))

    app.get('/', function(req, res) {
        res.sendFile(__dirname + '/public/html/index.html')
    })

    // var web3 = new Web3(new Web3.providers.HttpProvider('http://127.0.0.1:8545'))
    var web3 = new Web3('ws://127.0.0.1:8546')
    console.log('init web3js 1')
    var fromUser = _await(web3.eth.getAccounts())[0]
    console.log('FromUser:', fromUser)

    function isUnlocked(address) {
        try {
            _await(web3.eth.sign('', address))
        } catch (e) {
            console.log('need unlock')
            return false
        }
        console.log('unlock already')
        return true
    }
    function startProcess(callback) {
        var isUnlock = isUnlocked(fromUser)
        if (!isUnlock) {
            console.log('unlock is executing')
            _await(web3.eth.personal.unlockAccount(fromUser, '123'))
            console.log('unlock completed')
        }
        if (callback) {
            callback()
            console.log('submit transaction ...')
        }
    }

    var fileManagerContract = new web3.eth.Contract(AppConfig.abi, AppConfig.contractAddress)

    app.post(
        `${API_CTX}/account`,
        _async((req, res) => {
            console.log('/account', req.method, req.body)
            var user = req.body
            // Create account
            var accountAddress = _await(web3.eth.personal.newAccount(user.password))
            console.log('create account: ', accountAddress)
            // Transfer 1 ehter from master account to the new account
            startProcess(() =>
                _await(
                    web3.eth.sendTransaction(
                        {
                            from: fromUser,
                            to: accountAddress,
                            value: web3.utils.toWei('1', 'ether')
                        },
                        function(err, txHash) {
                            console.log('sendTransaction transfer:: ', txHash)
                            let accounts = mongoDb.collection('accounts')
                            transactions.insertOne({
                                address: accountAddress
                            })
                            let transactions = mongoDb.collection('transactions')
                            transactions.insertOne({
                                type: 'transfer',
                                to: accountAddress,
                                for: 'init_account',
                                amount: 1,
                                unit: 'ether'
                            })
                        }
                    )
                )
            )
            res.send({
                address: accountAddress
            })
        })
    )

    app.get(
        `${API_CTX}/accounts`,
        _async((req, res) => {
            console.log('/accounts', req.method, req.body)
            var accounts = _await(web3.eth.getAccounts())
            console.log('accounts::', accounts)
            let listAccounts = []
            for (let account of accounts) {
                listAccounts.push({
                    address: account,
                    balance: web3.utils.fromWei(_await(web3.eth.getBalance(account)), 'ether')
                })
            }
            res.send({
                accounts: listAccounts
            })
        })
    )

    app.get(
        `${API_CTX}/submit`,
        _async((req, res) => {
            var fileHash = req.query.hash
            var owner = req.query.owner
            var ownerAddress = req.query.ownerAddress
            console.log('submit info::', fileHash, owner, ownerAddress, fromUser, fileManagerContract.methods)
            startProcess(() =>
                fileManagerContract.methods
                    .trySave(fileHash, owner, ownerAddress, web3.utils.toWei('1', 'ether'))
                    .send(
                        {
                            from: fromUser,
                            gas: 4700000
                        },
                        (error, result) => {
                            console.log('callback::', error, result)
                        }
                    )
                    .on('transactionHash', function(transactionHash) {
                        console.log('transactionHash::', transactionHash)
                        res.send(transactionHash)
                    })
                    .on('receipt', function(receipt) {
                        console.log('receipt::', receipt)
                        let transactions = mongoDb.collection('transactions')
                        let files = mongoDb.collection('files')
                        const bOk = receipt.events && receipt.events.LogFileAddedStatus && receipt.events.LogFileAddedStatus.returnValues.status
                        // Insert transaction record
                        transactions.insertOne({
                            type: 'tx_save_file',
                            ownerAddress: ownerAddress,
                            fileHash: fileHash,
                            owner: owner,
                            status: bOk ? receipt.events.LogFileAddedStatus.returnValues.status : false,
                            transactionHash: receipt.transactionHash
                        })
                        // Insert file record
                        files.insertOne({
                            type: 'file',
                            ownerAddress: ownerAddress,
                            fileHash: fileHash,
                            owner: owner,
                            status: bOk ? receipt.events.LogFileAddedStatus.returnValues.status : false,
                            transactionHash: receipt.transactionHash
                        })
                        if (bOk) {
                            // Transfer 1 ehter from master account to the new account, if the file status is true
                            web3.eth.sendTransaction(
                                {
                                    from: fromUser,
                                    to: ownerAddress,
                                    value: web3.utils.toWei('1', 'ether')
                                },
                                function(err, txHash) {
                                    console.log('sendTransaction transfer:: ', txHash)
                                    transactions.insertOne({
                                        type: 'tx_transfer',
                                        to: ownerAddress,
                                        for: 'save_file',
                                        amount: 1,
                                        unit: 'ether',
                                        transactionHash: txHash
                                    })
                                }
                            )
                        }
                        io.send({
                            type: 'file',
                            LogFileAddedStatus: receipt.events ? receipt.events.LogFileAddedStatus : null
                        })
                    })
                    .on('error', console.error)
            )
        })
    )

    app.get(
        `${API_CTX}/file`,
        _async((req, res) => {
            var fileHash = req.query.hash
            var readerAddress = req.query.account
            console.log('fileHash::', fileHash, readerAddress)
            var details = _await(fileManagerContract.methods.tryRead(fileHash, readerAddress).call({ from: fromUser }))
            console.log(details)
            res.send(details)
        })
    )

    app.get(
        `${API_CTX}/files`,
        _async((req, res) => {
            const owner = req.query.owner
            const ownerAddress = req.query.ownerAddress
            let filter = {}
            if (owner) {
                filter.owner = owner
            }
            if (ownerAddress) {
                filter.ownerAddress = ownerAddress
            }
            mongoDb
                .collection('files')
                .find(filter)
                .toArray((err, docs) => {
                    console.log(docs)
                    res.send(docs)
                })
        })
    )

    app.get(
        `${API_CTX}/transaction`,
        _async((req, res) => {
            var transaction = _await(web3.eth.getTransaction(req.query.txId))
            console.log('Transaction: ', transaction)
            res.send(transaction)
        })
    )

    app.get(
        `${API_CTX}/transactions`,
        _async((req, res) => {
            const type = req.query.type
            const to = req.query.to
            const ownerAddress = req.query.ownerAddress
            let filter = {}
            if (type) {
                filter.type = type
            }
            if (to) {
                filter.to = to
            }
            if (ownerAddress) {
                filter.owner = ownerAddress
            }
            mongoDb
                .collection('transactions')
                .find(filter)
                .toArray((err, docs) => {
                    console.log(docs)
                    res.send(docs)
                })
        })
    )

    console.log('*************************')
    console.log('Server is ready!')
    console.log('*************************')
})()
